package com.test.platform.service.publish;

import com.test.platform.model.PublishInfo;
import com.test.platform.service.common.RepoConfigService;
import com.test.platform.service.common.GitCodeApiClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Service
public class PublishExecutionService {
    private static final Logger logger = LoggerFactory.getLogger(PublishExecutionService.class);

    @Value("${deveco.studio.path:D:/huawei/DevEcoStudio59300}")
    private String devecoPath;

    @Value("${har.workspace.path:E:/har}")
    private String harWorkspacePath;

    @Value("${ohpm.publish.password}")
    private String publishPassword;

    @Autowired
    private RepoConfigService repoConfigService;

    @Autowired
    private GitCodeApiClient gitCodeApiClient;

    private static final List<String> REQUIRED_FILES = Arrays.asList(
             "README.md", "LICENSE", "CHANGELOG.md"
    );

    private static final List<String> OPTIONAL_FILES = Arrays.asList(
            "README_zh.md", "README.OpenSource", "NOTICE", "TEST.md"
    );

    public boolean publishPackage(PublishInfo publishInfo, ProgressCallback callback) {
        String owner = publishInfo.getOwner();
        String name = publishInfo.getName();
        
        try {
            // 检查是否已经在发布中
            if (isPublishing(owner, name)) {
                String msg = String.format("包 %s/%s 正在发布中，请勿重复发布", owner, name);
                logger.warn(msg);
                if (callback != null) {
                    callback.onProgress(ExecutionStatus.FAILED, msg);
                }
                return false;
            }

            // 设置发布状态
            setPublishing(owner, name, true);

            try {
                // 原有的发布逻辑
                callback.onProgress(ExecutionStatus.PREPARING, "准备发布...");

                // 检查依赖
                if (!checkDependencies(callback)) {
                    logger.error("依赖检查失败");
                    return false;
                }

                // 克隆代码
                if (!cloneRepository(owner, name, callback)) {
                    logger.error("代码克隆失败");
                    return false;
                }

                // 修改项目配置
                callback.onProgress(ExecutionStatus.INSTALLING, "修改项目配置...");
                if (!modifyProjectConfig(name)) {
                    logger.error("项目配置修改失败");
                    return false;
                }

                // 安装依赖
                if (!installDependencies(name, callback)) {
                    logger.error("依赖安装失败");
                    return false;
                }

                // 复制必要文件
                callback.onProgress(ExecutionStatus.BUILDING, "复制必要文件...");
                if (!copyRequiredFiles(name)) {
                    logger.error("必要文件复制失败");
                    return false;
                }

                // 修改组织名称
                if (!modifyOrganizationName(name)) {
                    logger.error("组织名称修改失败");
                    return false;
                }

                // 构建HAR包
                if (!buildHarPackage(name, callback)) {
                    logger.error("HAR包构建失败");
                    return false;
                }

                // 发布包
                if (!publishHarPackage(name, callback)) {
                    logger.error("包发布失败");
                    return false;
                }
                callback.onProgress(ExecutionStatus.COMPLETED, "发布完成");

                // 添加版本信息缺失的仓库汇总
                List<String> missingVersionRepos = getMissingVersionRepositories();
                if (!missingVersionRepos.isEmpty()) {
                    StringBuilder summary = new StringBuilder("以下仓库缺少版本信息：\n");
                    for (String repo : missingVersionRepos) {
                        summary.append("- ").append(repo).append("\n");
                    }
                    callback.onProgress(ExecutionStatus.COMPLETED, summary.toString());
                }

                // 添加清理操作
                logger.info("开始清理临时文件");
                cleanupTempFiles(name);
                logger.info("清理完成");
                
                return true;
                

            } finally {
                // 清除发布状态
                setPublishing(owner, name, false);
            }

        } catch (Exception e) {
            String errorMsg = "发布过程发生错误: " + e.getMessage();
            logger.error(errorMsg, e);
            if (callback != null) {
                callback.onProgress(ExecutionStatus.FAILED, errorMsg);
            }
            return false;
        }
    }

    // 添加新方法来获取缺少版本信息的仓库列表
    private List<String> getMissingVersionRepositories() {
        try {
            List<Map<String, String>> repos = repoConfigService.loadRepoConfigs();
            List<String> missingVersionRepos = new ArrayList<>();
            
            for (Map<String, String> repo : repos) {
                String owner = repo.get("owner");
                String name = repo.get("name");
                
                // 从 changelog 获取版本信息
                Map<String, String> changelogInfo = gitCodeApiClient.fetchChangelog(owner, name, null);
                String version = changelogInfo.get("version");
                
                if (version == null || version.isEmpty()) {
                    missingVersionRepos.add(owner + "/" + name);
                }
            }
            
            return missingVersionRepos;
        } catch (Exception e) {
            logger.error("获取缺少版本信息的仓库列表失败", e);
            return new ArrayList<>();
        }
    }

    private boolean checkDependencies(ProgressCallback callback) {
        return executeCommand("git --version", callback, ExecutionStatus.PREPARING) && 
               executeCommand("node -v", callback, ExecutionStatus.PREPARING) && 
               executeCommand("ohpm -v", callback, ExecutionStatus.PREPARING);
    }

    private boolean cloneRepository(String owner, String name, ProgressCallback callback) {
        // 确保工作目录存在
        File workspaceDir = new File(harWorkspacePath);
        if (!workspaceDir.exists()) {
            boolean created = workspaceDir.mkdirs();
            if (!created) {
                logger.error("无法创建工作目录: {}", harWorkspacePath);
                return false; // 或者抛出异常，根据业务需求决定
            }
        }
    
        String tempDir = harWorkspacePath + "/" + name;
        String actualOwner = owner;
        String actualRepo = name;
        String subDir = null;
    
        // 处理特殊情况：openharmony_tpc_samples 仓库
        if ("openharmony_tpc_samples".equals(owner)) {
            actualOwner = "openharmony-tpc";
            actualRepo = "openharmony_tpc_samples";
            subDir = name; // 原始的 name 变成子目录
            tempDir = harWorkspacePath + "/" + actualRepo;
        } else {
            // 普通情况：直接使用原始的 owner 和 name
            actualOwner = owner;
            actualRepo = name;
        }

        // 检查目标目录是否已存在
        File cloneTargetDir = new File(tempDir);
        if (cloneTargetDir.exists() && cloneTargetDir.isDirectory()) {
            logger.info("目标目录 {} 已存在，跳过克隆步骤", tempDir);
            return true;  // 如果目录已存在，直接返回成功
        }
    
        // 构建克隆命令
        String cloneCommand;
        if ("openharmony-tpc".equals(actualOwner) && "openharmony_tpc_samples".equals(actualRepo)) {
            cloneCommand = String.format(
                "git clone https://gitcode.com/%s/%s.git --recurse-submodules %s",
                actualOwner, actualRepo, tempDir
            );
        } else {
            cloneCommand = String.format(
                "git clone https://gitcode.com/%s/%s.git %s",
                actualOwner, actualRepo, tempDir
            );
        }
    
        // 执行克隆命令
        boolean cloneSuccess = executeCommand(cloneCommand, callback, ExecutionStatus.CLONING);
        if (!cloneSuccess) {
            return false;
        }
    
        // 如果是子目录情况，需要将子目录复制到最终目标位置
        if (subDir != null) {
            String sourceDir = tempDir + "/" + subDir;
            String targetDir = harWorkspacePath + "/" + subDir;
            
            try {
                // 如果目标目录已存在，先删除
                File targetDirFile = new File(targetDir);
                if (targetDirFile.exists()) {
                    Files.walk(targetDirFile.toPath())
                         .sorted((p1, p2) -> -p1.compareTo(p2))
                         .map(Path::toFile)
                         .forEach(File::delete);
                }
    
                // 复制子目录到目标位置
                Files.walk(Paths.get(sourceDir))
                     .forEach(source -> {
                         Path target = Paths.get(targetDir, source.toString()
                             .substring(sourceDir.length()));
                         try {
                             Files.copy(source, target);
                         } catch (IOException e) {
                             logger.error("复制文件失败: {}", e.getMessage());
                         }
                     });
    
                // 删除临时克隆的完整仓库
                Files.walk(Paths.get(tempDir))
                     .sorted((p1, p2) -> -p1.compareTo(p2))
                     .map(Path::toFile)
                     .forEach(File::delete);
    
                return true;
            } catch (IOException e) {
                logger.error("处理子目录失败: {}", e.getMessage());
                return false;
            }
        }
    
        return true;
    }

    private boolean modifyProjectConfig(String projectName) {
        Path projectPath = Paths.get(harWorkspacePath, projectName);
        
        
        try {
            // 修改 build-profile.json5
            // Path buildProfilePath = projectPath.resolve("build-profile.json5");
            // String content = new String(Files.readAllBytes(buildProfilePath));
            // content = content.replaceAll("\"compileSdkVersion\":.*\\n", "")
            //                .replaceAll("\"targetSdkVersion\":.*\\n", "")
            //                .replaceAll("\"compatibleSdkVersion\": \"[^\"]*\"", "\"compatibleSdkVersion\": \"5.0.3(15)\"")
            //                .replaceAll("\"runtimeOS\": \"OpenHarmony\"", "\"runtimeOS\": \"HarmonyOS\"");
            // Files.write(buildProfilePath, content.getBytes());

            // 创建 local.properties 文件
            Path localPropertiesPath = projectPath.resolve("local.properties");
            String localPropertiesContent = "sdk.dir=D:\\huawei\\SDK\\openharmony";
            Files.write(localPropertiesPath, localPropertiesContent.getBytes());
            logger.info("创建 local.properties 文件成功");

            // 处理 hvigor 目录下的文件
            Path hvigorPath = projectPath.resolve("hvigor");
            if (Files.exists(hvigorPath)) {
                // 删除 hvigor-wrapper.js
                Files.deleteIfExists(hvigorPath.resolve("hvigor-wrapper.js"));

                // 修改 hvigor-config.json5
                Path hvigorConfigPath = hvigorPath.resolve("hvigor-config.json5");
                if (Files.exists(hvigorConfigPath)) {
                    String hvigorConfig = new String(Files.readAllBytes(hvigorConfigPath));
                    // 删除 hvigorVersion 字段
                    hvigorConfig = hvigorConfig.replaceAll("\"hvigorVersion\"\\s*:\\s*\"[^\"]*\"\\s*,?\\s*\\n?", "");
                    // 删除 @ohos/hvigor-ohos-plugin 字段
                    hvigorConfig = hvigorConfig.replaceAll("\"@ohos/hvigor-ohos-plugin\"\\s*:\\s*\"[^\"]*\"\\s*,?\\s*\\n?", "");
                    // 添加或更新 modelVersion
                    if (!hvigorConfig.contains("\"modelVersion\"")) {
                        hvigorConfig = hvigorConfig.replaceFirst("\\{", "{\n    \"modelVersion\": \"5.0.3\",");
                    } else {
                        hvigorConfig = hvigorConfig.replaceAll("\"modelVersion\"\\s*:\\s*\"[^\"]*\"", "\"modelVersion\": \"5.0.3\"");
                    }
                    Files.write(hvigorConfigPath, hvigorConfig.getBytes());
                }
            }

            // 修改项目根目录的 oh-package.json5
            Path ohPackagePath = projectPath.resolve("oh-package.json5");
            if (Files.exists(ohPackagePath)) {
                String ohPackageContent = new String(Files.readAllBytes(ohPackagePath));
                if (!ohPackageContent.contains("\"modelVersion\"")) {
                    ohPackageContent = ohPackageContent.replaceFirst("\\{", "{\n    \"modelVersion\": \"5.0.3\",");
                } else {
                    ohPackageContent = ohPackageContent.replaceAll("\"modelVersion\"\\s*:\\s*\"[^\"]*\"", "\"modelVersion\": \"5.0.3\"");
                }
                Files.write(ohPackagePath, ohPackageContent.getBytes());
            }

        } catch (IOException e) {
            logger.error("修改配置文件失败", e);
            return false;
        }

        return true;
    }

    private boolean installDependencies(String projectName, ProgressCallback callback) {
        return executeCommand(String.format(
            "cd %s/%s && ohpm install --all --registry https://ohpm.openharmony.cn/ohpm --strict_ssl false",
            harWorkspacePath, projectName
        ), callback, ExecutionStatus.INSTALLING);
    }

    private boolean copyRequiredFiles(String projectName) {
        Path projectPath = Paths.get(harWorkspacePath, projectName);
        Path libraryPath = projectPath.resolve("library");

        // 确保目标目录存在
        try {
            Files.createDirectories(libraryPath);
        } catch (IOException e) {
            logger.error("创建library目录失败", e);
            return false;
        }

        for (String file : REQUIRED_FILES) {
            try {
                Path sourcePath = projectPath.resolve(file);
                if (Files.exists(sourcePath)) {
                    Files.copy(sourcePath, libraryPath.resolve(file));
                } else {
                    logger.warn("文件 {} 不存在，跳过复制", file);
                    continue;  // 跳过不存在的文件，继续处理下一个
                }
            } catch (IOException e) {
                logger.error("复制文件 {} 失败", file, e);
                continue;  // 某个文件复制失败时继续处理其他文件
            }
        }

        // 复制可选文件
        for (String file : OPTIONAL_FILES) {
            try {
                Path sourcePath = projectPath.resolve(file);
                if (Files.exists(sourcePath)) {
                    Files.copy(sourcePath, libraryPath.resolve(file));
                }
            } catch (IOException e) {
                logger.warn("复制可选文件 {} 失败", file, e);
            }
        }
        return true;
    }

    private boolean modifyOrganizationName(String projectName) {
        try {
            // 使用完整路径
            Path ohPackagePath = Paths.get(harWorkspacePath, projectName, "library", "oh-package.json5");

            // 确保文件存在
            if (!Files.exists(ohPackagePath)) {
                logger.error("oh-package.json5 文件不存在: {}", ohPackagePath);
                return false;
            }
            String content = new String(Files.readAllBytes(ohPackagePath));
            content = content.replace("@ohos", "@summer");
            Files.write(ohPackagePath, content.getBytes());
            return true;
        } catch (IOException e) {
            logger.error("修改组织名称失败", e);
            return false;
        }
    }

    private boolean buildHarPackage(String projectName, ProgressCallback callback) {
        // 检查项目目录是否存在
        Path projectPath = Paths.get(harWorkspacePath, projectName);
        if (!Files.exists(projectPath)) {
            logger.error("项目目录不存在: {}", projectPath);
            return false;
        }
 
        String cdCommand = String.format("cd /d \"%s\"", projectPath.toString());

        // 先执行同步命令
        String syncCommand = String.format(
            "%s && \"%s\\tools\\node\\node.exe\" \"%s\\tools\\hvigor\\bin\\hvigorw.js\" --sync -p product=default --analyze=normal --parallel --incremental --daemon",
            cdCommand,
            devecoPath,
            devecoPath
        );

        logger.info("开始同步项目配置，执行命令: {}", syncCommand);
        if (!executeCommand(syncCommand, callback, ExecutionStatus.BUILDING)) {
            logger.error("项目配置同步失败");
            return false;
        }

         String buildCommand = String.format(
             "%s && \"%s\\tools\\node\\node.exe\" \"%s\\tools\\hvigor\\bin\\hvigorw.js\" --mode module -p product=default -p module=library@default assembleHar --analyze=normal --parallel --incremental --daemon",
             cdCommand,
             devecoPath,
             devecoPath
         );
         
         logger.info("开始构建HAR包，执行命令: {}", buildCommand);
         boolean result = executeCommand(buildCommand, callback, ExecutionStatus.BUILDING);
        if (!result) {
            logger.error("HAR包构建失败");
        } else {
            logger.info("HAR包构建成功");
        }
        return result;
    }

    private boolean publishHarPackage(String projectName, ProgressCallback callback) {
        Path outputPath = Paths.get(harWorkspacePath, projectName, "library", "build", "default", "outputs", "default");
        if (!Files.exists(outputPath)) {
            logger.error("HAR包输出目录不存在: {}", outputPath);
            return false;
        }

        // 检查 library.har 文件是否存在
        Path harFile = outputPath.resolve("library.har");
        if (!Files.exists(harFile)) {
            logger.error("HAR文件不存在: {}", harFile);
            return false;
        }

        // 使用 Windows CMD 执行命令
        // 修改 ProcessBuilder 配置以支持交互式窗口
        ProcessBuilder processBuilder = new ProcessBuilder()
            .command("cmd", "/c", String.format(
                "cd /d \"%s\" && " +
                "set NODE_OPTIONS=--max-old-space-size=16384 && " +
                "ohpm publish library.har",
                outputPath.toAbsolutePath().toString()
            ))
            .redirectErrorStream(true)
            .directory(new File(harWorkspacePath))
            .inheritIO();

        try {
            Process process = processBuilder.start();
            StringBuilder outputBuffer = new StringBuilder();
            
            // 修改输入处理线程
            Thread inputThread = new Thread(() -> {
                try (BufferedWriter writer = new BufferedWriter(
                        new OutputStreamWriter(process.getOutputStream(), "GBK"))) {
                    
                    while (!Thread.currentThread().isInterrupted() && process.isAlive()) {
                        String currentOutput;
                        synchronized (outputBuffer) {
                            currentOutput = outputBuffer.toString();
                        }
                        
                        // 检查是否需要输入密码
                        if (currentOutput.contains("what is your passphrase")) {
                            if (callback != null) {
                                String userPassword = null;
                                boolean confirmed = callback.onConfirmation("请输入发布密码");
                                if (confirmed && publishPassword != null && !publishPassword.isEmpty()) {
                                    userPassword = publishPassword;
                                }
                                
                                if (userPassword != null) {
                                    writer.write(userPassword + "\n");
                                    writer.flush();
                                    logger.info("用户已输入密码");
                                    Thread.sleep(1000);
                                } else {
                                    logger.warn("用户取消了密码输入");
                                    process.destroy();
                                    break;
                                }
                            }
                        }
                        
                        Thread.sleep(100);
                    }
                } catch (IOException | InterruptedException e) {
                    if (!(e instanceof InterruptedException)) {
                        logger.error("处理输入流失败: {}", e.getMessage());
                    }
                    process.destroy();
                    Thread.currentThread().interrupt();
                }
            });
            inputThread.start();

            // 输出读取线程保持不变
            Thread outputThread = new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(
                        new InputStreamReader(process.getInputStream(), "GBK"))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        synchronized (outputBuffer) {
                            outputBuffer.append(line).append("\n");
                        }
                        logger.info(line);
                        if (callback != null) {
                            callback.onProgress(ExecutionStatus.PUBLISHING, line);
                        }
                    }
                } catch (IOException e) {
                    logger.error("读取输出流失败: {}", e.getMessage());
                }
            });
            outputThread.start();

            // 等待进程完成
            boolean success = process.waitFor() == 0;
            
            // 停止输入输出线程
            outputThread.interrupt();
            inputThread.interrupt();
            
            if (!success) {
                logger.error("包发布失败，完整输出：\n{}", outputBuffer.toString());
                if (callback != null) {
                    callback.onProgress(ExecutionStatus.FAILED, 
                        "包发布失败，详细信息：" + outputBuffer.toString());
                }
                process.destroy(); // 确保进程被终止
            } else {
                logger.info("包发布成功");
                if (callback != null) {
                    callback.onProgress(ExecutionStatus.COMPLETED, "包发布成功");
                }
            }
            
            return success;
        } catch (Exception e) {
            logger.error("执行发布命令失败: {}", e.getMessage());
            if (callback != null) {
                callback.onProgress(ExecutionStatus.FAILED, "发布命令执行异常: " + e.getMessage());
            }
            return false;
        }
    }

    private void cleanupTempFiles(String projectName) {
        try {
            Path projectPath = Paths.get(harWorkspacePath, projectName);
            if (Files.exists(projectPath)) {
                logger.info("开始清理目录: {}", projectPath);
                Files.walk(projectPath)
                     .sorted((p1, p2) -> -p1.compareTo(p2))
                     .map(Path::toFile)
                     .forEach(File::delete);
                logger.info("目录清理完成: {}", projectPath);
            } else {
                logger.info("目录不存在，无需清理: {}", projectPath);
            }
        } catch (IOException e) {
            logger.error("清理临时文件失败: {}", e.getMessage());
        }
    }

    // 添加进度状态枚举
    public enum ExecutionStatus {
        PREPARING("准备中"),
        CLONING("正在克隆代码"),
        INSTALLING("安装依赖"),
        BUILDING("构建中"),
        PUBLISHING("发布中"),
        COMPLETED("完成"),
        FAILED("失败");

        private final String description;
        
        ExecutionStatus(String description) {
            this.description = description;
        }
        
        public String getDescription() {
            return description;
        }
    }

    // 添加进度回调接口
    public interface ProgressCallback {
        void onProgress(ExecutionStatus status, String message);
        boolean onConfirmation(String message);
    }

    private boolean executeCommand(String command, ProgressCallback callback, ExecutionStatus currentStatus) {
        try {
            ProcessBuilder processBuilder;
            if (callback != null) {
                callback.onProgress(currentStatus != null ? currentStatus : ExecutionStatus.PREPARING, 
                    "准备执行命令: " + command);
            }

            // 统一使用 CMD
            processBuilder = new ProcessBuilder()
                .command("cmd", "/c", command)
                .redirectErrorStream(true)
                .directory(new File(harWorkspacePath));
            
            Process process = processBuilder.start();
            StringBuilder outputBuffer = new StringBuilder();  // 添加输出缓冲
            
            // 读取命令输出
            try (BufferedReader reader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    outputBuffer.append(line).append("\n");  // 保存输出
                    logger.info(line);
                    if (callback != null) {
                        callback.onProgress(currentStatus, line);
                    }
                }
            }
            
            boolean success = process.waitFor() == 0;
            
            if (!success) {
                // 命令失败时输出完整日志
                logger.error("命令执行失败，完整输出：\n{}", outputBuffer.toString());
                if (callback != null) {
                    callback.onProgress(ExecutionStatus.FAILED, 
                        "命令执行失败，详细信息：" + outputBuffer.toString());
                }
            }
            
            return success;
        } catch (Exception e) {
            if (e.getMessage().contains("Connection reset by peer")) {
                logger.warn("客户端连接已断开: {}", e.getMessage());
            } else {
                logger.error("执行命令失败: {}", e.getMessage());
            }
            
            if (callback != null) {
                callback.onProgress(ExecutionStatus.FAILED, 
                    "命令执行异常: " + e.getMessage());
            }
            return false;
        }
    }

    // 添加用于跟踪发布状态的成员变量和方法
    private final Map<String, Boolean> publishingPackages = new ConcurrentHashMap<>();

    private String getPublishKey(String owner, String name) {
        return owner + "/" + name;
    }

    private boolean isPublishing(String owner, String name) {
        return publishingPackages.getOrDefault(getPublishKey(owner, name), false);
    }

    private void setPublishing(String owner, String name, boolean status) {
        String key = getPublishKey(owner, name);
        if (status) {
            publishingPackages.put(key, true);
        } else {
            publishingPackages.remove(key);
        }
    }
}